#ifndef LOG_MONITOR_H
#define LOG_MONITOR_H

#include <boost/signal.hpp>
#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>
#include "utils/mutex.hpp"
#include "utils/lock.hpp"
#include "utils/condition.hpp"
#include "log_message_block.h"
#include "log_message_pool.h"

#include <deque>
#include <cassert>

namespace vpm
{
	namespace log 
	{
		using namespace vpm::log::detail;
		//log monitor is a daemon thread that perform the real log operations. 
		//wirte file, write to console , write so socket , etc. 
		// it's an observer pattern , appenders should check the message's logflag 
		// to log the message or not. 
		class LogMonitor
		{
			typedef std::deque<LogMessageBlock *>  LogMsgQueue;
			typedef boost::signal<void (LogMessageBlock*)>  WriteLogSignal; 

			LogMonitor()
			{
				m_bRunning = false;
			}	

		public:
			static LogMonitor & instance();

			~LogMonitor()
			{
				printf("left message count :%d\n",m_inQueue.size() + m_outQueue.size());
			}

			int start_monitor()
			{
				m_bRunning = true;
				boost::thread monitorThread(boost::bind(&LogMonitor::svc,this));
				return 0; 
			}

			void stop_monitor()
			{		
				m_bRunning = false; 
				m_queueCond.signal();
			}

			void post_message(LogMessageBlock * pLogMsg)
			{		
				WriteLock lock(m_inMutex);		
				assert(pLogMsg->length()> 0);
				m_inQueue.push_back(pLogMsg);
				m_queueCond.signal();
			}

			LogMessagePool & message_pool()
			{
				return m_logMsgPool;
			}

			//for the monitor is running in single thread, we needn't lock on outQueue

			void svc()
			{
				while (m_bRunning)
				{
					{
						WriteLock lock(m_inMutex);
						if (m_inQueue.empty())
						{	
							m_queueCond.wait(m_inMutex);					
							if (!m_bRunning )
							{
								break; 
							}
						}

						if (m_outQueue.empty())
						{
							m_outQueue.swap(m_inQueue);
						}
					}

					while (!m_outQueue.empty())
					{
						// here , write to logger. 
						LogMessageBlock * pLogMsg = m_outQueue.front(); 
						m_outQueue.pop_front();
						if (pLogMsg->length() == 0)
						{
							assert(pLogMsg->length()> 0);
						}
						//write pLogMsg 
						m_writeLogSignal(pLogMsg);
					}			
				}
				delete  m_logMonitor;
			}

			void attach(WriteLogSignal::slot_function_type  subscriber)
			{
				m_writeLogSignal.connect(subscriber);
			}

		private:
			LogMsgQueue m_inQueue; 
			LogMsgQueue m_outQueue;
			bool m_bRunning; 
			Mutex m_inMutex;  //lock in queue	
			Condition m_queueCond; 

			WriteLogSignal m_writeLogSignal;

			LogMessagePool m_logMsgPool;
			static LogMonitor  *m_logMonitor; 	
		};
	}
}



#endif // 
   
  
  
